﻿using System;
using System.Collections;
using System.Reflection;
using CI.Interfaces;
using System.Runtime.Serialization;
using CI.Organism;

namespace CI.Organism
{
    /// <summary>
    /// Base class for learning rules, declares properties and create new synapse
    /// </summary>
    [DataContract(IsReference = true)]
    public abstract class LearningRule
    {
        #region Properties
        /// <summary>
        /// Learning rate of gain which makes the connections weight stronger
        /// </summary>
        public double GainLearningRate
        {
            get { return _gainLearningRate; }
        }
        /// <summary>
        /// Learning rate of gain
        /// </summary>
        [DataMember]
        protected double _gainLearningRate;

        /// <summary>
        /// Learning rate of loss which makes the connections weight weaker
        /// </summary>
        public double LossLearningRate
        {
            get { return _lossLearningRate; }
        }
        /// <summary>
        /// Learning rate of loss
        /// </summary>
        [DataMember]
        protected double _lossLearningRate;

        /// <summary>Learning rate GUID used to uniquely identify it in the neural system</summary>
        [DataMember]
        public Guid Guid { get; set; }

        /// <summary>
        /// synapse this learning rule applies to
        /// </summary>
        [DataMember]
        protected ISynapse _synapse;

        #endregion

        protected static SimEnv SimEnv { get { return SimEnv.Instance; } }

        /// <summary>
        /// Create a new learning rule
        /// </summary>
        /// <param name="mutationRate">mutation rate</param>
        /// <param name="childSynapse">child synapse the learning rule will end up in</param>
        /// <returns>new cell</returns>
        public static ILearningRule CreateNewLearningRule(double mutationRate, ISynapse childSynapse)
        {
            #region Argument check
            if (childSynapse == null)
                throw new ArgumentException("synapse");
            #endregion

            // mutate adding new cells
            // we need to reflect and see what genes are available
            ArrayList learningRules = Globals.GetConcreteClasses(typeof(ILearningRule));


            // save its type
            Type classType = (Type)learningRules[Mutation.CollectionSelect(learningRules)];
            // We are anticipating a constructor that takes 4 parameters,
            Type[] conTypes = new Type[4];
            conTypes[0] = typeof(double);
            conTypes[1] = typeof(double);
            conTypes[2] = typeof(ISynapse);
            conTypes[3] = typeof(Guid);

            // Get the constructor that adheres to our parameters.
            ConstructorInfo constructor = classType.GetConstructor(conTypes);
            // Define the parameters to pass into our constructor when we call it.
            object[] constructorParams = new object[4];
            constructorParams[0] = Mutation.MutateCrawlOrLeap((double)0, mutationRate);
            constructorParams[1] = Mutation.MutateCrawlOrLeap((double)0, mutationRate);
            constructorParams[2] = childSynapse;
            constructorParams[3] = Guid.NewGuid();
            SimEnv.Instance.TraceLogger.LogDebug(string.Format("Invoking constructor of type {0}", classType));

            // Invoke the constructor dynamically, getting an instance of the class.  
            ILearningRule learningRule = constructor.Invoke(constructorParams) as ILearningRule;

            // mutate the learning rule
            learningRule = learningRule.Mutate(mutationRate, childSynapse);

            return learningRule;
        }

        /// <summary>
        /// do a equals comparison of one learning type to another only comparing the value types, no refference comparisons
        /// </summary>
        /// <param name="learningRule">learning rule to compare against</param>
        /// <returns>true if equal value types</returns>
        public bool EqualsValueTypes(ILearningRule learningRule)
        {
            // check the learning rule values
            if (learningRule == null)
                return false;
            if (GainLearningRate != learningRule.GainLearningRate)
                return false;
            if (LossLearningRate != learningRule.LossLearningRate)
                return false;
            if (Guid != learningRule.Guid)
                return false;

            return true;
        }
    }
}
